package router

import (
	"core/component/logger"
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/golang/protobuf/proto"
	"math"
	"sync"
)

type MsgHandler func(msgId uint16, data []byte)

type GatewayOnlineHandler func(msgId uint16, data []byte)

type OnlinePvpHandler func(msgId uint16, data []byte)

type RoomSrvHandler func(msgId uint16, data []byte)

type WorldHandler func(msgId uint16, data []byte)

// ForwardMsghandler 转发的消息处理函数
type ForwardMsghandler func(srvID string, userID uint64, msgID uint16, data []byte)

type Router struct {
	littleEndian       bool
	msgHandlers        map[uint16]MsgHandler     //玩家消息
	roomSrvHandler     map[uint16]RoomSrvHandler //房间消息
	worldHandler       map[uint16]WorldHandler   //world消息
	forwardMsgHandlers *sync.Map
}

func NewRouter() *Router {
	r := &Router{
		littleEndian:       true,
		msgHandlers:        make(map[uint16]MsgHandler),
		roomSrvHandler:     make(map[uint16]RoomSrvHandler),
		worldHandler:       make(map[uint16]WorldHandler),
		forwardMsgHandlers: &sync.Map{},
	}
	return r
}

func (r *Router) Route(data []byte) (uint16, error) {
	if len(data) < 2 {
		return 0, errors.New("protobuf data too short")
	}
	var msgId uint16
	if r.littleEndian {
		msgId = binary.LittleEndian.Uint16(data[2:])
	} else {
		msgId = binary.BigEndian.Uint16(data[2:])
	}
	handler, ok := r.msgHandlers[msgId]
	if !ok {
		return msgId, errors.New("msgHandlers not find cmd:")
	}
	handler(msgId, data)
	return msgId, nil
}

func (r *Router) Register(msgId uint16, msgHandler MsgHandler) bool {
	if msgId > math.MaxUint16 {
		return false
	}
	if handler, ok := r.msgHandlers[msgId]; ok {
		logger.Errorf("error", handler)
		return false
	}
	r.msgHandlers[msgId] = msgHandler
	return true
}

func (r *Router) RegisterRoomSrv(msgId uint16, msgHandler RoomSrvHandler) bool {
	if handler, ok := r.roomSrvHandler[msgId]; ok {
		logger.Errorf("error", handler)
		return false
	}
	r.roomSrvHandler[msgId] = msgHandler
	return true
}

func (r *Router) RouterRoomSrvMsg(data []byte) (uint16, error) {
	if len(data) < 4 {
		return 0, errors.New("protobuf data too short")
	}
	var msgId uint16
	if r.littleEndian {
		msgId = binary.LittleEndian.Uint16(data[2:])
	} else {
		msgId = binary.BigEndian.Uint16(data[2:])
	}
	handler, ok := r.roomSrvHandler[msgId]
	if !ok {
		return msgId, errors.New("")
	}
	handler(msgId, data)
	return msgId, nil
}

func (r *Router) RegisterWorld(msgId uint16, msgHandler WorldHandler) bool {
	if handler, ok := r.worldHandler[msgId]; ok {
		logger.Errorf("error", handler)
		return false
	}
	r.worldHandler[msgId] = msgHandler
	return true
}

func (r *Router) RouterWorldMsg(data []byte, param interface{}) (uint16, error) {
	if len(data) < 4 {
		return 0, errors.New("protobuf data too short")
	}
	var msgId uint16
	if r.littleEndian {
		msgId = binary.LittleEndian.Uint16(data[2:])
	} else {
		msgId = binary.BigEndian.Uint16(data[2:])
	}
	handler, ok := r.worldHandler[msgId]
	if !ok {
		return msgId, errors.New("")
	}
	handler(msgId, data)
	return msgId, nil
}

func (r *Router) Marshal(msgId uint16, msg interface{}) ([]byte, error) {
	pbMsg, ok := msg.(proto.Message)
	if !ok {
		return []byte{}, fmt.Errorf("msg is not protobuf message")
	}
	// data
	data, err := proto.Marshal(pbMsg)
	if err != nil {
		return data, err
	}
	// 4byte = len(flag)[2byte] + len(msgID)[2byte]
	buf := make([]byte, 4+len(data))
	if r.littleEndian {
		binary.LittleEndian.PutUint16(buf[0:2], 0)
		binary.LittleEndian.PutUint16(buf[2:], msgId)
	} else {
		binary.BigEndian.PutUint16(buf[0:2], 0)
		binary.BigEndian.PutUint16(buf[2:], msgId)
	}
	copy(buf[4:], data)
	return buf, err
}

func (r *Router) UnMarshal(data []byte, msg interface{}) error {
	if len(data) < 4 {
		return errors.New("protobuf data too short")
	}
	pbMsg, ok := msg.(proto.Message)
	if !ok {
		return fmt.Errorf("msg is not protobuf message")
	}
	return proto.Unmarshal(data[4:], pbMsg)
}

// InnerServerMarshal Marshal goroutine safe 内部服务器通信
func (r *Router) InnerServerMarshal(msgID uint16, msg interface{}) ([]byte, error) {
	pbMsg, ok := msg.(proto.Message)
	if !ok {
		return []byte{}, fmt.Errorf("msg is not protobuf message")
	}
	// data
	data, err := proto.Marshal(pbMsg)
	if err != nil {
		return data, err
	}

	// 4byte = len(flag)[2byte] + len(msgID)[2byte]
	buf := make([]byte, 2+len(data))
	if r.littleEndian {
		binary.LittleEndian.PutUint16(buf[0:2], msgID)
	} else {
		binary.BigEndian.PutUint16(buf[0:2], msgID)
	}
	copy(buf[2:], data)
	return buf, err
}

// RegisterForwardHandler 注册转发的消息处理方法
func (r *Router) RegisterForwardHandler(msgID uint16, msgHandler ForwardMsghandler) bool {
	if msgID >= math.MaxUint16 {
		logger.Errorf("too many protobuf messages (max = %v)", math.MaxUint16)
		return false
	}

	_, ok := r.forwardMsgHandlers.Load(msgID)
	if ok {
		logger.Warningf("message %v is already registered", msgID)
	}
	r.forwardMsgHandlers.Store(msgID, msgHandler)
	return true
}

// ForwardRoute 路由转发的消息处理器(通常经过Route路由后返回错误则通过ForwardRoute路由转发消息)
func (r *Router) ForwardRoute(srvID string, userID uint64, data []byte) (uint16, error) {

	if len(data) < 2 {
		return 0, errors.New("protobuf data too short")
	}

	var msgID uint16
	if r.littleEndian {
		msgID = binary.LittleEndian.Uint16(data[2:])
	} else {
		msgID = binary.BigEndian.Uint16(data[2:])
	}

	handler, ok := r.forwardMsgHandlers.Load(msgID)

	if ok && handler != nil {
		msgHandler, ok := handler.(ForwardMsghandler)
		if ok {
			msgHandler(srvID, userID, msgID, data)
		}
	} /* else {
	//if r.defaultHandler != nil {
	//	r.defaultHandler(msgID, data)
	//	return msgID, nil
	}*/
	//return msgID, errors.New("unknow msg msgID:" + strconv.Itoa(int(msgID)))
	//}
	return msgID, nil
}

// Clear 清除Router注册的消息处理器
func (r *Router) Clear() {
	r.msgHandlers = nil
	r.roomSrvHandler = nil
	r.worldHandler = nil
}
